<?php

namespace youmbe\phpword;

use Yii;
use yii\helpers\ArrayHelper;
use yii\data\ActiveDataProvider;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\i18n\Formatter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style\ListItem as ListItemStyle;
use PhpOffice\Common\Font;
use PhpOffice\PhpWord\SimpleType\TextAlignment;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Shared\Converter;
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
use PhpOffice\PhpWord\Style\TablePosition;
use youmbe\phpword\widgets\DetailView;
use PhpOffice\PhpWord\Shared\Html as PhpWordHtml;
use youmbe\phpword\assets\WordAsset;
use youmbe\phpword\widgets\TableView;
use youmbe\phpword\widgets\GridView;
use PhpOffice\PhpSpreadsheet\Style\Font as FontStyle;
use PhpOffice\PhpWord\SimpleType\TblWidth as TblWidthSimpleType;
use PhpOffice\PhpWord\SimpleType\JcTable;

/**
 * Word Widget for generate Word File or for load Word File.
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class WordDoc_1 extends \youmbe\phpword\base\Widget {

    /**
     * @var boolean for set the export excel with multiple sheet.
     */
    public $isMultipleSection = false;

    /**
     * @var boolean Enables different header for odd and even pages.
     */
    public $evenAndOddHeaders = false;

    /**
     * @var array properties for set property on the excel object.
     */
    public $properties = [
        'creator' => 'My name',
        'company' => 'My factory',
        'title' => 'My title',
        'description' => 'My description',
        'category' => 'My category',
        'lastModifiedBy' => 'My name',
        'subject' => 'My subject',
        'keywords' => 'my, key, word',
    ];

    /**
     * @var int 创建时间
     */
    public $created_at;

    /**
     * @var int 修改时间
     */
    public $updated_at;

    /**
     * @var \common\logics\word\contract\Contract object or DataProvider object with much data.
     */
    public $model;

    /**
     * @var array columns to get the attributes from the model, this valid value only the exist attribute on the model.
     * If this is not set, then all attribute of the model will be set as columns.
     */
    public $columns = [];

    /**
     * @var array header to set the header column on first line. Set this if want to custom header.
     * If not set, the header will get attributes label of model attributes.
     */
    public $headers = [];

    /**
     * @var string|array name for file name to export or save.
     */
    public $fileName;

    /**
     * @var string save path is a directory to save the file or you can blank this to set the file as attachment.
     */
    public $savePath;

    /**
     * @var boolean to set the import data will return as array.
     */
    public $asArray;

    /**
     * @var boolean define the column autosize
     */
    public $autoSize = false;

    /**
     * Setting properties for word file
     * @param PHPWord $objectWord
     * @param array $properties
     */
    public function properties(&$objectWord, $properties = []) {
        $docInfo = $objectWord->getDocInfo();
        foreach ($properties as $key => $value) {
            $keyname = "set" . ucfirst($key);
            $docInfo->{$keyname}($value);
        }
        $docInfo->setCreated($this->created_at ?: time());
        $docInfo->setModified($this->updated_at ?: time());
    }

    /**
     * 执行输出
     * @param PhpWord $objectWord
     * @param \common\logics\word\contract\Contract $model
     */
    public function executeRender($objectWord, $model = null) {
        if ($model == null) {
            $model = $this->model;
        }
        $cover = $model->cover;
        if ($cover) {
            $section = $objectWord->addSection();
            $this->renderCover($section, $cover);
        }
        $toc = $model->toc;
        if ($toc) {
//            $sectionStyles = [
//                'headerHeight' => 50,
//                'footerHeight' => 50,
//            ];
            $sectionStyles = null;
            $section = $objectWord->addSection($sectionStyles);
            $this->renderToc($section, $toc);
            $this->addHeader($section);
        }

        $documents = $model->documents;
        foreach ($documents as $doc) {
            $pageContent = $doc->toArray();
            $type = ArrayHelper::getValue($pageContent, 'type');
//          $renderMethod = 'renderPage' . ucfirst($type);
//          if ($this->hasMethod($renderMethod) && is_callable($renderMethod)) {
//             $this->{$renderMethod}($section, $pageContent);
//          }
            $section = $objectWord->addSection();
            if ($type == 'content') {
                $this->renderActivePage($section, $doc);
//                $this->renderPage($section, $pageContent);
            } elseif ($type == 'sign') {
                $this->renderSign($section, $doc);
            } else {
                $this->renderAttach($section, $pageContent);
            }
        }
    }

    /**
     * 输出签署页
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param \common\logics\word\contract\ContractPage $data
     */
    public function renderSign($section, $data) {
        $title = ArrayHelper::getValue($data, 'title');
        $titleOptions = ArrayHelper::getValue($data, 'titleOptions');
        $titleLevel = ArrayHelper::getValue($titleOptions, 'level');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        /* @var $model \common\logics\word\contract\Contract */
        $model = $data->contract;
        widgets\DirectionGridView::widget([
            'direction' => widgets\DirectionGridView::DIRECTION_VERTICAL,
            'break' => 0,
            'options' => [
                'unit' => TblWidthSimpleType::PERCENT,
            ],
            'rowOptions' => [
                'fontStyle' => [
                    'size' => 14,
                ],
            ],
            'paragraphStyle' => [
                'indentation' => [
                    'firstLine' => 0,
                ],
                'indent' => 0,
            ],
            'section' => $section,
            'dataProvider' => new \yii\data\ActiveDataProvider([
                'query' => $model->getContractParties(),
                'pagination' => false
            ]),
            'columns' => [
                [
                    'attribute' => 'signTag',
                    'contentOptions' => [
                        'height' => 70
                    ]
                ],
                [
                    'attribute' => 'signConsignee',
                    'contentOptions' => [
                        'height' => 70
                    ]
                ],
                'signDate',
                'address',
                'contact',
                'telephone',
                'entity.fax',
                'email',
                'entity.bank',
                'entity.bank_no',
                'entity.unified_no',
                'entity.bank_code',
                'entity.telephone',
            ],
        ]);
    }

    /**
     * 输出封面
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     */
    public function renderCover($section, $data) {
        $title = ArrayHelper::getValue($data, 'title');
        if ($title) {
            $fontStyleName = '封面标题';
            $section->addText($title, $fontStyleName, $fontStyleName);
        }
        $columns = ArrayHelper::getValue($data, 'columns');
        DetailView::widget([
            'section' => $section,
            'model' => $this->model,
            'attributes' => $columns,
            'tableOptions' => [
                'width' => 100,
                'position' => [
                    'tblpXSpec' => TablePosition::XALIGN_CENTER,
                    'tblpYSpec' => TablePosition::YALIGN_CENTER,
                ],
            ],
            'cellStyle' => [
                'valign' => 'center',
                'borderColor' => 'ffffff',
                'borderSize' => 0,
            ],
            'unit' => 'auto',
            'rowOptions' => [
                'height' => 36,
            ],
            'contentOptions' => [
                'fontStyle' => [
                    'size' => 16,
                    'bold' => true,
                    'underline' => FontStyle::UNDERLINE_SINGLE
                ],
            ],
            'fontStyle' => [
                'name' => '华文中宋',
                'size' => 16,
                'bold' => true
            ],
            'paragraphStyle' => [
                'indentation' => [
                    'firstLine' => 0,
                ],
                'indent' => 0,
            ],
        ]);
    }

    /**
     * 输出目录
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $styles
     */
    public function renderToc($section) {
        // Add text elements
        $section->addText('目录', '目录标题', '目录标题');
        $tocStyle = [
//            'tabLeader' => \PhpOffice\PhpWord\Style\TOC::TAB_LEADER_DOT,
//            'tabPos' => 12,
            'indent' => 0,
        ];
        $minDepth = 1;
        $maxDepth = 4;
        $section->addTOC('toc 1', $tocStyle, $minDepth, $maxDepth);
//        $section->addTOC($fontStyle, $tocStyle, $minDepth, $maxDepth);
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $name
     */
    public function registerNumberingStyle($section, $name = null) {
        $objectWord = $this->view;
        if (!$name) {
            $id = $section->getSectionId();
            $name = 'MultiLevel' . $id;
        } else {
            $objectWord->addTitleStyle(2, [
                'name' => '黑体',
                'size' => 14,
            ], [
                'numStyle' => $name,
                'numLevel' => 0,
                'indentation' => [
                    'firstLine' => Font::pointSizeToTwips(28),
                ],
                'spacing' => Font::pointSizeToTwips(24),
                'spacingLineRule' => LineSpacingRule::EXACT,
                'spaceBefore' => Font::pointSizeToTwips(14),
                'spaceAfter' => Font::pointSizeToTwips(14),
            ]);
        }
        $levels = static::getMultiLevelStyle();
        $objectWord->addNumberingStyle($name, [
            'type' => 'multilevel',
            'levels' => $levels,
        ]);
        return $name;
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param \common\logics\word\contract\ContractPage $data
     */
    public function renderActivePage(&$section, $data) {
        $section->getStyle()->setPageNumberingStart(1);
        //添加页脚
        $this->addFooter($section);

        $listStyle = $this->registerNumberingStyle($section, 'NumerLevel');
        $title = ArrayHelper::getValue($data, 'title');
        $introduction = ArrayHelper::getValue($data, 'introduction');
        $titleOptions = ArrayHelper::getValue($data, 'titleOptions');
        $titleLevel = ArrayHelper::getValue($titleOptions, 'level');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        $parties = $this->model->parties;
        if (!empty($parties)) {
            foreach ($parties as $party) {
                $tag = $party['shipText'];
                if ($party['tag']) {
                    $tag .= '（' . $party['tag'] . '）：';
                }
                $section->addText($tag, [
                    'name' => '黑体',
                    'size' => 14,
                ]);
                $section->addText($party['name'], 'underline');
            }
            $section->addTextBreak();
        }
        if ($introduction) {
            $section->addText($introduction);
        }

        widgets\MutilListView::widget([
            'section' => $section,
            'dataProvider' => new \yii\data\ActiveDataProvider([
                'query' => $data->getContents(),
                'pagination' => false
            ]),
            'listStyle' => $listStyle,
        ]);
    }

    /**
     * 添加页眉
     * @param \PhpOffice\PhpWord\Element\Section $section
     */
    public function addHeader(&$section) {
        $header = $section->addHeader();
        $pStyle = [
            'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::RIGHT,
            'indentation' => [
                'firstLine' => 0,
                'left' => 0,
            ],
            'lineHeight' => 1,
            'spacingLineRule' => LineSpacingRule::AUTO,
            'suppressAutoHyphens' => true,
        ];
        $header->addText('济南云贝网络科技有限公司', [
            'name' => '幼圆',
            'size' => 8,
            'allCaps' => true,
        ], $pStyle);
        $header->addText('Jinan Youmbe Network Sci-Tech Co., Ltd.', 'Header', 'Header');

        $header->addImage('http://oss.youmbe.com/brand/word.png', [
            'width' => 66.85,
            'wrappingStyle' => \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_INFRONT, //Wrapping style, inline, square, tight, behind, or infront.
            'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::START,
            'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE,
            'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_LEFT,
            'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_MARGIN,
            'top' => 37.2,
            'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE,
            'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE,
//            'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_VERTICAL_BOTTOM,
//            'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_TMARGIN,
        ]);

        if ($this->evenAndOddHeaders !== false) {
            $headerEven = $section->addHeader(\PhpOffice\PhpWord\Element\Header::EVEN);
            $tableEven = $headerEven->addTable();
            $tableEven->addRow();
            $tableEven->addCell(4500)->addImage('http://oss.youmbe.com/brand/word.png', [
                'width' => 80,
                'height' => 80,
                'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END
            ]);
            $tableEven->addCell(4500)->addText('This is the header');
        }
    }

    /**
     * 添加页脚
     * @param \PhpOffice\PhpWord\Element\Section $section
     */
    public function addFooter(&$section) {
        $section->createFooter()->addPreserveText('{PAGE}', 'Footer', 'Footer');
        if ($this->evenAndOddHeaders !== false) {
            $section->addFooter(\PhpOffice\PhpWord\Element\Footer::EVEN)->addPreserveText('{PAGE}', 'Footer', 'Footer');
        }
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     */
    public function renderAttach(&$section, $data) {
        $title = ArrayHelper::getValue($data, 'title');
        $titleOptions = ArrayHelper::getValue($data, 'titleOptions');
        $titleLevel = ArrayHelper::getValue($titleOptions, 'level');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        $content = ArrayHelper::getValue($data, 'contents', []);
        $listStyle = $this->registerNumberingStyle($section);
        static::dumpItem($section, $content, $listStyle);
    }

    /**
     * (non-PHPdoc)
     * @see \yii\base\Widget::run()
     */
    public function run() {
        if (!$this->model) {
            throw new InvalidConfigException('The "model" must be either a Format object or a configuration array.');
        }
        $view = $this->view;
        $view->getSettings()->setEvenAndOddHeaders($this->evenAndOddHeaders);
        if (isset($this->properties)) {
            $this->properties($view, $this->properties);
        }
        //设置文档使用样式
        $this->registerAssets();
        //输出内容
        $this->executeRender($view);
        //导出文档
        return $this->render($this->fileName);
    }

    public function registerAssets() {
        $this->getAssetBundle()->registerStyle();
    }

    public static function getListStyles() {
        return [
            [
                'font' => [
                    'name' => '黑体',
                    'size' => 14,
                ],
                'list' => [
                    'pStyle' => 'Heading2',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
//                    'hint' => 'eastAsia', //编号文本类型 Font content type, default, eastAsia, or cs.
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                    'alignment ' => \PhpOffice\PhpWord\SimpleType\Jc::BOTH,
                    'font' => '黑体',
//                    'restart' => true, //重新编号
//                    'start' => '', //编号
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                    'spaceBefore' => Font::pointSizeToTwips(14),
                    'spaceAfter' => Font::pointSizeToTwips(14),
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading3',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading4',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.%3.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ]
        ];
    }

    public static function getMultiLevelStyle($level = null, $name = null) {
        $listStyles = static::getListStyles();
        if ($level === null) {
            return ArrayHelper::getColumn($listStyles, 'list');
        }
        $styles = ArrayHelper::getValue($listStyles, $level);
        if ($name) {
            return ArrayHelper::getValue($styles, $name);
        }
        return $styles;
    }

    /**
     * 按层级列表输出
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     * @param integer $listStyle
     * @param integer $level
     */
    public function dumpItem(&$section, $data, $listStyle, $useNumberTitle = false, $level = 0) {
        foreach ($data as $item) {
            $title = ArrayHelper::getValue($item, 'title', '');
            if (!$level && $useNumberTitle) {
                $section->addTitle($title, 2);
            } else {
                $styles = static::getMultiLevelStyle($level);
                $section->addListItem($title, $level, $styles['font'], $listStyle, $styles['paragraph']);
            }
            $content = ArrayHelper::getValue($item, 'content', '');
            $contentFormat = ArrayHelper::getValue($item, 'format');
            if ($content) {
                if ($contentFormat == 'html') {
                    PhpWordHtml::addHtml($section, $content, false, false);
                } elseif ($contentFormat == 'table') {
                    GridView::widget([
                        'options' => [
                            'unit' => TblWidthSimpleType::PERCENT,
                        ],
                        'paragraphStyle' => [
                            'indentation' => [
                                'firstLine' => 0,
                            ],
                            'indent' => 0,
                            'lineHeight' => 1,
                        ],
                        'section' => $section,
                        'dataProvider' => new \yii\data\ActiveDataProvider([
                            'query' => $this->model->getDevices(),
                            'pagination' => false
                        ]),
                        'columns' => [
                            [
                                'attribute' => 'name',
                                'headerOptions' => [
                                    'width' => 15,
                                ],
                            ],
                            [
                                'attribute' => 'spec',
                                'headerOptions' => [
                                    'width' => 30,
                                ],
                            ],
                            [
                                'attribute' => 'unit',
                                'headerOptions' => [
                                    'width' => 10,
                                    'align' => 'right',
                                ],
                                'contentOptions' => [
                                    'align' => 'right'
                                ]
                            ],
                            [
                                'attribute' => 'num',
                                'headerOptions' => [
                                    'width' => 10,
                                    'align' => 'right',
                                ],
                                'contentOptions' => [
                                    'align' => 'right'
                                ]
                            ],
                            [
                                'attribute' => 'price',
                                'headerOptions' => [
                                    'width' => 15,
                                    'align' => 'right',
                                ],
                                'contentOptions' => [
                                    'align' => 'right'
                                ]
                            ],
                            [
                                'attribute' => 'amount',
                                'headerOptions' => [
                                    'width' => 15,
                                    'align' => 'right',
                                ],
                                'contentOptions' => [
                                    'align' => 'right'
                                ]
                            ],
                        ],
                    ]);
                } else {
                    //$section->addText($content);
                    $section->addPreserveText($content);
                }
            }
            $items = ArrayHelper::getValue($item, 'items', []);
            if (empty($items)) {
                continue;
            }
            $this->dumpItem($section, $items, $listStyle, $useNumberTitle, $level + 1);
        }
    }

}
